/*
 JNI wrapper for C++ libraries.
 Author: Daniil Smelov
*/


#include <jni.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include "log_stream.h"
#include "map_loader.h"
#include "map_container.h"
#include "map_index.h"
#include "map_datums.h"
#include "map_render.h"
//#include "render.h"

#include "SDL.h"


static map_index* map_collection = NULL; 

static map_render* maprender = NULL; 

static int surface_dx = 0;
static int surface_dy = 0;

/*--------------------------------------------------------------------------*/
#ifdef __cplusplus
extern "C" {
#endif

/*--------------------------------------------------------------------------*/
JNIEXPORT void JNICALL
Java_com_swampex_Swampex_initDatums( _JNIEnv* env, jobject thiz,
                                              jstring filename);

JNIEXPORT void JNICALL
Java_com_swampex_Swampex_freeDatums( _JNIEnv* env, jobject thiz);

JNIEXPORT void JNICALL
Java_com_swampex_Swampex_showDatums( _JNIEnv* env, jobject thiz);

JNIEXPORT void JNICALL
Java_com_swampex_Swampex_initMaps( _JNIEnv* env, jobject thiz,
                                              jstring path, jint dx, jint dy);

JNIEXPORT jintArray JNICALL
Java_com_swampex_Swampex_getMapSurface( _JNIEnv* env, jobject thiz);

JNIEXPORT void JNICALL
Java_com_swampex_Swampex_scrollMap(_JNIEnv* env, jobject thiz,
									jint delta_x, jint delta_y);


JNIEXPORT jdouble JNICALL
Java_com_swampex_Swampex_getZoom(_JNIEnv* env, jobject thiz);

JNIEXPORT jdouble JNICALL
Java_com_swampex_Swampex_getZoomNext(_JNIEnv* env, jobject thiz);

JNIEXPORT jdouble JNICALL
Java_com_swampex_Swampex_getZoomPrev(_JNIEnv* env, jobject thiz);

JNIEXPORT void JNICALL
Java_com_swampex_Swampex_zoomIn(_JNIEnv* env, jobject thiz);


JNIEXPORT void JNICALL
Java_com_swampex_Swampex_zoomOut(_JNIEnv* env, jobject thiz);

JNIEXPORT void JNICALL
Java_com_swampex_Swampex_nextMap(_JNIEnv* env, jobject thiz);

JNIEXPORT void JNICALL
Java_com_swampex_Swampex_prevMap(_JNIEnv* env, jobject thiz);

JNIEXPORT void JNICALL
Java_com_swampex_Swampex_setLocation(_JNIEnv* env, jobject thiz,
									jdouble lat, jdouble lon);


JNIEXPORT jdouble JNICALL
Java_com_swampex_Swampex_getLocationLat(_JNIEnv* env, jobject thiz);

JNIEXPORT jdouble JNICALL
Java_com_swampex_Swampex_getLocationLon(_JNIEnv* env, jobject thiz);

JNIEXPORT jint JNICALL
Java_com_swampex_Swampex_getXbyLatLon(_JNIEnv* env, jobject thiz, jdouble lat, jdouble lon);

JNIEXPORT jint JNICALL
Java_com_swampex_Swampex_getYbyLatLon(_JNIEnv* env, jobject thiz, jdouble lat, jdouble lon);

JNIEXPORT void JNICALL
Java_com_swampex_Swampex_cleanup( _JNIEnv* env, jobject thiz);


#ifdef __cplusplus
}
#endif


/*--------------------------------------------------------------------------*/
JNIEXPORT void JNICALL
Java_com_swampex_Swampex_showDatums( _JNIEnv* env, jobject thiz)
{
	mapdatums_debug();
}

/*--------------------------------------------------------------------------*/
JNIEXPORT void JNICALL
Java_com_swampex_Swampex_initDatums( _JNIEnv* env, jobject thiz,
                                              jstring filename)
{
    const char* filename_utf8 = env->GetStringUTFChars(filename, false);

	mapdatums_init((char*)filename_utf8);
}

/*--------------------------------------------------------------------------*/
JNIEXPORT void JNICALL
Java_com_swampex_Swampex_initMaps( _JNIEnv* env, jobject thiz,
                                              jstring path, jint dx, jint dy)
{
    const char* filename_utf8 = env->GetStringUTFChars(path, false);

	surface_dx = dx;
	surface_dy = dy;

   	if(env->ExceptionOccurred()) 
   		logstream_write("exception: %s, line: %s", __FILE__, __LINE__);

	map_collection = new map_index((char*)filename_utf8);

	maprender = new map_render(map_collection, surface_dx, surface_dy);

	double lat = 0; //60.382313;
	double lon = 0; //29.562426;

	maprender->center_set(lat, lon);

    if (filename_utf8)
    {
        env->ReleaseStringUTFChars(path, filename_utf8);

		if(env->ExceptionOccurred()) 
			logstream_write("exception: %s, line: %s", __FILE__, __LINE__);
	}

}

/*--------------------------------------------------------------------------*/
JNIEXPORT jint JNICALL
Java_com_swampex_Swampex_getXbyLatLon(_JNIEnv* env, jobject thiz, jdouble lat, jdouble lon)
{
	double latitude = lat;
	double longitude = lon;

	long x, y;

	maprender->ll_to_xy_onmap(lat, lon, &x, &y);		

	return (jint)x;
}

/*--------------------------------------------------------------------------*/
JNIEXPORT jint JNICALL
Java_com_swampex_Swampex_getYbyLatLon(_JNIEnv* env, jobject thiz, jdouble lat, jdouble lon)
{
	double latitude = lat;
	double longitude = lon;

	long x, y;

	maprender->ll_to_xy_onmap(lat, lon, &x, &y);		

	return (jint)y;
}


/*--------------------------------------------------------------------------*/
JNIEXPORT void JNICALL
Java_com_swampex_Swampex_nextMap(_JNIEnv* env, jobject thiz)
{
	maprender->map_next();
}

/*--------------------------------------------------------------------------*/
JNIEXPORT void JNICALL
Java_com_swampex_Swampex_prevMap(_JNIEnv* env, jobject thiz)
{
	maprender->map_prev();
}

/*--------------------------------------------------------------------------*/
JNIEXPORT jdouble JNICALL
Java_com_swampex_Swampex_getLocationLat(_JNIEnv* env, jobject thiz)
{
	double lat;
	double lon;
	maprender->center_get(&lat, &lon);

	return lat;
}

/*--------------------------------------------------------------------------*/
JNIEXPORT jdouble JNICALL
Java_com_swampex_Swampex_getLocationLon(_JNIEnv* env, jobject thiz)
{
	double lat;
	double lon;
	maprender->center_get(&lat, &lon);

	return lon;
}

/*--------------------------------------------------------------------------*/
JNIEXPORT jdouble JNICALL
Java_com_swampex_Swampex_getZoom(_JNIEnv* env, jobject thiz)
{
	return
	maprender->zoom_get();
}

/*--------------------------------------------------------------------------*/
JNIEXPORT jdouble JNICALL
Java_com_swampex_Swampex_getZoomNext(_JNIEnv* env, jobject thiz)
{
	return
	maprender->zoom_get_next();
}

/*--------------------------------------------------------------------------*/
JNIEXPORT jdouble JNICALL
Java_com_swampex_Swampex_getZoomPrev(_JNIEnv* env, jobject thiz)
{
	return
	maprender->zoom_get_prev();
}

/*--------------------------------------------------------------------------*/
JNIEXPORT void JNICALL
Java_com_swampex_Swampex_setLocation(_JNIEnv* env, jobject thiz,
									jdouble lat, jdouble lon)
{
	double lt = lat;
	double lg = lon;
	maprender->center_set(lt, lg);
}


/*--------------------------------------------------------------------------*/
JNIEXPORT void JNICALL
Java_com_swampex_Swampex_scrollMap(_JNIEnv* env, jobject thiz,
									jint delta_x, jint delta_y)
{
	maprender->scroll_by(delta_x, delta_y);
}

/*--------------------------------------------------------------------------*/
JNIEXPORT void JNICALL
Java_com_swampex_Swampex_zoomIn(_JNIEnv* env, jobject thiz)
{
	maprender->zoom_up();
}

/*--------------------------------------------------------------------------*/
JNIEXPORT void JNICALL
Java_com_swampex_Swampex_zoomOut(_JNIEnv* env, jobject thiz)
{
	maprender->zoom_down();
}


/*--------------------------------------------------------------------------*/
JNIEXPORT jintArray JNICALL
Java_com_swampex_Swampex_getMapSurface( _JNIEnv* env, jobject thiz)
{
	SDL_Surface* surface = maprender->surface();

    jintArray pixels =   env->NewIntArray(surface_dx * surface_dy);

//	env->SetIntArrayRegion(pixels, 0, 480*480, (const jint*)surface->pixels );
	jint* ptr = env->GetIntArrayElements(pixels, 0);                                                                      

	if(env->ExceptionOccurred()) 
		logstream_write("exception: %s, line: %s", __FILE__, __LINE__);

	int n = surface_dx * surface_dy * sizeof(int);

	memcpy(ptr, surface->pixels, n);                                                                      
	
	env->ReleaseIntArrayElements(pixels, ptr, 0);

	if(env->ExceptionOccurred()) 
		logstream_write("exception: %s, line: %s", __FILE__, __LINE__);

#if 0
	for (int i = 0; i < surface->height(); i++)
	{
		for (int j = 0; j < surface->width(); j++)
		{
            unsigned char a, r, g, b;

			a = surface->getA(j, i);
			r = surface->getR(j, i);
			g = surface->getG(j, i);
			b = surface->getB(j, i);

            unsigned long color =
                ((unsigned long)a << 24) |
                ((unsigned long)r << 16) |
                ((unsigned long)g << 8) |
                ((unsigned long)b);


            jint result = (unsigned long)color;

			env->SetIntArrayRegion(pixels, i* surface->width() + j, 1, &result );
		}
    }
#endif

    return pixels;
}


/*--------------------------------------------------------------------------*/
JNIEXPORT void JNICALL
Java_com_swampex_Swampex_freeDatums( _JNIEnv* env, jobject thiz)
{
	mapdatums_done();
}

/*--------------------------------------------------------------------------*/
JNIEXPORT void JNICALL
Java_com_swampex_Swampex_cleanup( _JNIEnv* env, jobject thiz)
{

	if ( maprender )
	{
		delete maprender;
		maprender = NULL;
	}
	
	if ( map_collection )
	{
		delete map_collection;
		map_collection = NULL;
	} 

	mapdatums_done();
}

/*--------------------------------------------------------------------------*/
jint JNICALL JNI_OnLoad (JavaVM * vm, void * reserved) 
{
    return JNI_VERSION_1_4;
}
